#ifndef DATADEFINE_H
#define DATADEFINE_H
#include <string>
#include <sstream>
#include <map>
#include <vector>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/serialization/string.hpp>
#include <boost/serialization/map.hpp>
#include "Define.h"
using namespace std;

namespace gitstore {

class StoreObject;

class ErrTypeNotMatch : std::exception {
    virtual const char* what() const _GLIBCXX_USE_NOEXCEPT override final {
        return "Operation and Type Not Match";
    }
};

class StoreObject {

    using String = std::string;
    using Map = std::map<String, String>;
    using List = std::vector<String>;

    using InitializerList = std::initializer_list<String>;
    using InitializerMap = std::initializer_list<std::pair<const String, String>>;

    using MapIterator = Map::iterator;
    using ListIterator = List::iterator;

public:
    StoreObject();
    StoreObject(StoreObjectType type);

    StoreObject(const StoreObject& object);
    StoreObject(StoreObject&& object);

    StoreObject& operator=(const StoreObject& object);
    StoreObject& operator=(StoreObject&& object);

    explicit StoreObject(const String &svalue, StoreObjectType type = StoreObjectType::String);
    explicit StoreObject(String &&svalue, StoreObjectType type = StoreObjectType::String);

    explicit StoreObject(const char* cvalue);
    explicit StoreObject(const char* cvalue, size_t size);

    explicit StoreObject(const List& vec);
    explicit StoreObject(List&& vec);
    explicit StoreObject(const InitializerList& ilist);

    explicit StoreObject(const Map& mp);
    explicit StoreObject(Map&& mp);
    explicit StoreObject(const InitializerMap& mlist);

    ~StoreObject();

private:
    StoreObjectType _type;

    union {
        String *str;
        List *list;
        Map *map;
    } _value;

    inline void releaseString();
    inline void releaseBlob();
    inline void releaseMap();
    inline void releaseList();

public:
    bool isString() const;
    bool isBlob() const;
    bool isList() const;
    bool isMap() const;
    void resetType(StoreObjectType type);

public:
    const String& operator [](size_t index) const;
    const String& operator [](const String& key) const;

    String& operator [](size_t index);
    String& operator [](const String& key);

    StoreObjectType getType() const;

    bool isKeyExist(const String& key) const;

    String get(size_t index) const;
    String get(const String& key) const;
    String get() const;

    String at(size_t index) const;
    String at(const String& key) const;
    String at() const;

    void set(const char* cvalue);
    void set(const char* cvalue, size_t size);

    void set(const String& svalue);
    void set(String&& svalue);

    void clear();

    size_t size() const;
    MapIterator mbegin() const;
    MapIterator mend() const;

    ListIterator vbegin() const ;
    ListIterator vend() const ;

    template<typename...Args>
    void emplace_back(Args&&...args);

    String getSerializedValue() const;
    void fromSerializedValue(const String& value);
    void fromSerializedValue(String&& value);

private:
    static const String _null;
};

template<typename...Args>
void StoreObject::emplace_back(Args&&...args) {
    if ( _type != StoreObjectType::List ) {
        throw ErrTypeNotMatch();
    }
    _value.list->emplace_back(std::forward<Args>(args)...);
}
/*
template <typename T>
class CNodeValue {
public:
    CNodeValue();
    CNodeValue(const T& value);
    CNodeValue(T&& value);
    CNodeValue(const CNodeValue& value);
    CNodeValue(CNodeValue&& value);

public:
    virtual ~CNodeValue();

public:


    void setValue(const T& value);
    void setValue(T&& value);

    const T* getValue() const;
    T* getValue();
    std::string getSerializedValue() const;
    void fromSerializedValue(const std::string& value);
    void fromSerializedValue(std::string&& value);
protected:
    T _data;
};

template <typename T>
CNodeValue<T>::CNodeValue() {

}

template <typename T>
CNodeValue<T>::CNodeValue(const T& value)
    : _data(value) {

}

template <typename T>
CNodeValue<T>::CNodeValue(T&& value)
    : _data(std::move(value)) {

}

template <typename T>
CNodeValue<T>::CNodeValue(const CNodeValue& value) {
    _data = value._data;
}

template <typename T>
CNodeValue<T>::CNodeValue(CNodeValue&& value)
    : _data(std::move(value._data)){
}

template <typename T>
CNodeValue<T>::~CNodeValue() {

}

template <typename T>
void CNodeValue<T>::setValue(const T& value) {
    _data = value;
}

template <typename T>
void CNodeValue<T>::setValue(T&& value) {
    _data = value;
}

template <typename T>
const T* CNodeValue<T>::getValue() const{
    return &_data;
}

template <typename T>
T* CNodeValue<T>::getValue() {
    return &_data;
}

template <typename T>
std::string CNodeValue<T>::getSerializedValue() const {
    std::ostringstream ss;
    boost::archive::binary_oarchive ob(ss);
    ob << _data;
    return ss.str();
}

template <typename T>
void CNodeValue<T>::fromSerializedValue(const std::string& value) {
    std::istringstream ss(value);
    boost::archive::binary_iarchive ob(ss);
    ob >> _data;
}

template <typename T>
void CNodeValue<T>::fromSerializedValue(std::string&& value) {
    std::istringstream ss(std::forward<std::string>(value));
    boost::archive::binary_iarchive ob(ss);
    ob >> _data;
}

using String = std::string;
using Blob = std::string;
using Map = std::map<std::string, std::string>;
using List = std::vector<std::string>;

using CNodeValueString = CNodeValue<String>;
using CNodeValueBlob = CNodeValue<Blob>;
using CNodeValueMap = CNodeValue<Map>;
using CNodeValueList = CNodeValue<List>;
*/
//class CNodeValueBuilder {
//public:
//    static auto buildValue(StoreObjectType type) {
//        switch (type) {
//        case StoreObjectType::String:
//            return CNodeValueString;
//        case StoreObjectType::Blob:
//            return CNodeValueBlob;
//        case StoreObjectType::Map:
//            return CNodeValueMap;
//        case StoreObjectType::List:
//            return CNodeValueList;
//        case StoreObjectType::UnKnown:
//            return CNodeValueBlob;
//        }
//    }
//};
}
#endif
